package sensimulator.intersection;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JFrame;

import sensimulator.intersection.lanes.CarLane;
import sensimulator.intersection.lanes.CyclistLane;
import sensimulator.intersection.lanes.Lane;
import sensimulator.intersection.lanes.PedestrianLane;
import sensimulator.intersection.traffic.Car;
import sensimulator.intersection.traffic.TrafficUser;

/**
 * Controls the intersection and displays it
 * 
 * @author Erwin
 * 
 */
public class Intersection extends JFrame {

	public static final int WIDTH = 800;

	public static final int HEIGHT = 800;

	private Painter painter;

	private ArrayList<Lane> lanes;

	public Intersection() {
		super();

		setSize(WIDTH, HEIGHT);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setContentPane(painter = new Painter(this));
		setTitle("Traffic Simulator Groep3");

		try { setupLanes(); } 
		catch (Exception e) { System.out.println("Error while settting up lanes: " + e.getMessage()); }
		
		setVisible(true);
	}

	public void tick(long time, int delta) {
		for (Lane lane : lanes)
			lane.tick(time, delta);
		
		detectCollisions();
		
		repaint(1);
	}
	
	private void detectCollisions() {
		for (Lane lane : lanes) {
			synchronized(lane.getTraffic()) {
				for (TrafficUser user : lane.getTraffic()) {
					if (user.getClass().equals(Car.class) && user.getSpeed() > 0)
						detectCollisionsWith(user);
				}
			}
		}
	}
	
	private void detectCollisionsWith(TrafficUser user) {
		//  Check traffic in every lane, except for the same lane the user is in
		for (Lane lane : lanes) {
			if (lane == user.getLane())
				continue;

			synchronized(lane.getTraffic()) {
				// Check traffic this user can collide with
				for (Iterator<TrafficUser> i = lane.getTraffic().iterator(); i.hasNext(); ) {
					TrafficUser other = i.next();
					if (other.getClass().equals(Car.class) || other.isDisposed())
						continue;
					
					if (user.collidesWith(other)) {
						addDrawable(new Bloodpool(other.getPosition()));
						other.dispose();
					}
					
					if (other.isDisposed())
						i.remove();
				}
			}
		}
		
	}

	private void setupLanes() throws Exception {
		lanes = new ArrayList<Lane>();

		// South Left
		Lane SL = new CarLane(1, this);
		SL.addNode(new Point(425, 950), false);
		SL.addNode(new Point(425, 660), true);
		SL.addNode(new Point(425, 655), false);
		SL.addNode(new Point(425, 470), false);
		SL.addNode(new Point(280, 320), false);
		SL.addNode(new Point(-150, 320), false);
		SL.addTrafficLight(new Point(425, 627));
		
		// South Up
		Lane SU = new CarLane(2, this);
		SU.addNode(new Point(475, 950), false);
		SU.addNode(new Point(475, 660), true);
		SU.addNode(new Point(475, 655), false);
		SU.addNode(new Point(475, -150), false);
		SU.addTrafficLight(new Point(475, 627));

		// South Right
		Lane SR = new CarLane(3, this);
		SR.addNode(new Point(525, 950), false);
		SR.addNode(new Point(525, 660), true);
		SR.addNode(new Point(525, 655), false);
		SR.addNode(new Point(525, 540), false);
		SR.addNode(new Point(630, 475), false);
		SR.addNode(new Point(950, 475), false);
		SR.addTrafficLight(new Point(525, 627));
		
		// Cyclists South Up
		Lane SUC = new CyclistLane(4, this);
		SUC.addNode(new Point(562, 950), false);
		SUC.addNode(new Point(562, 660), true);
		SUC.addNode(new Point(562, 655), false);
		SUC.addNode(new Point(562, -150), false);
		SUC.addTrafficLight(new Point(555, 627));
		
		// Cyclists South Right
		Lane SRC = new CyclistLane(5, this);
		SRC.addNode(new Point(562, 950), false);
		SRC.addNode(new Point(562, 660), true);
		SRC.addNode(new Point(562, 655), false);
		SRC.addNode(new Point(562, 600), false);
		SRC.addNode(new Point(580, 560), false);
		SRC.addNode(new Point(950, 560), false);
		SRC.addTrafficLight(new Point(570, 627));
		
		// Pedestrians South Right
		Lane SRP = new PedestrianLane(28, this);
		SRP.addNode(new Point(210, 850), false);
		SRP.addNode(new Point(210, 625), true);
		SRP.addNode(new Point(215, 625), false);
		SRP.addNode(new Point(375, 625), false);
		SRP.addTrafficLight(new Point(185, 635));
		
		// Pedestrians South Right Reverse
		Lane SRPR = new PedestrianLane(28, this, false);
		SRPR.addNode(new Point(375, 625), false);
		SRPR.addNode(new Point(370, 625), true);
		SRPR.addNode(new Point(365, 625), false);
		SRPR.addNode(new Point(210, 625), false);
		SRPR.addNode(new Point(210, 850), false);
		SRPR.addTrafficLight(new Point(350, 635));

		// Pedestrians South Left
		Lane SLP = new PedestrianLane(6, this);
		SLP.addNode(new Point(585, 850), false);
		SLP.addNode(new Point(585, 625), true);
		SLP.addNode(new Point(580, 625), false);
		SLP.addNode(new Point(375, 625), false);
		SLP.addTrafficLight(new Point(610, 635));
		
		// Pedestrians South Left Reverse
		Lane SLPR = new PedestrianLane(6, this, false);
		SLPR.addNode(new Point(375, 625), false);
		SLPR.addNode(new Point(380, 625), true);
		SLPR.addNode(new Point(385, 625), false);
		SLPR.addNode(new Point(585, 625), false);
		SLPR.addNode(new Point(585, 850), false);
		SLPR.addTrafficLight(new Point(390, 635));
		
		SRP.setFollowUp(SLPR);
		SLP.setFollowUp(SRPR);
		
		lanes.add(SL);
		lanes.add(SU);
		lanes.add(SR);
		lanes.add(SUC);
		lanes.add(SRC);
		lanes.add(SRP);
		lanes.add(SRPR);
		lanes.add(SLP);
		lanes.add(SLPR);

		lanes.add(SL.clone(15, 180));
		lanes.add(SU.clone(16, 180));
		lanes.add(SR.clone(17, 180));
		lanes.add(SUC.clone(18, 180));
		lanes.add(SRC.clone(19, 180));
		
		Lane NRP = SRP.clone(14, 180);
		Lane NRPR = SRPR.clone(14, 180, false);
		Lane NLP = SLP.clone(20, 180);
		Lane NLPR = SLPR.clone(20, 180, false);

		NRP.setFollowUp(NLPR);
		NLP.setFollowUp(NRPR);
		
		lanes.add(NRP);
		lanes.add(NRPR);
		lanes.add(NLP);
		lanes.add(NLPR);
		
		lanes.add(SL.clone(8, 90));
		lanes.add(SU.clone(9, 90));
		lanes.add(SR.clone(10, 90));
		lanes.add(SUC.clone(11, 90));
		lanes.add(SRC.clone(12, 90));
		
		Lane ERP = SRP.clone(7, 90);
		Lane ERPR = SRPR.clone(7, 90, false);
		Lane ELP = SLP.clone(13, 90);
		Lane ELPR = SLPR.clone(13, 90, false);
		
		ERP.setFollowUp(ELPR);
		ELP.setFollowUp(ERPR);
		
		lanes.add(ERP);
		lanes.add(ERPR);
		lanes.add(ELP);
		lanes.add(ELPR);
		
		lanes.add(SL.clone(22, 270));
		lanes.add(SU.clone(23, 270));
		lanes.add(SR.clone(24, 270));
		lanes.add(SUC.clone(25, 270));
		lanes.add(SRC.clone(26, 270));
		
		Lane WRP = SRP.clone(21, 270);
		Lane WRPR = SRPR.clone(21, 270, false);
		Lane WLP = SLP.clone(27, 270);
		Lane WLPR = SLPR.clone(27, 270, false);
		
		WRP.setFollowUp(WLPR);
		WLP.setFollowUp(WRPR);
		
		lanes.add(WRP);
		lanes.add(WRPR);
		lanes.add(WLP);
		lanes.add(WLPR);
	}

	public ArrayList<Lane> getLanes() {
		return lanes;
	}

	public void addDrawable(Drawable d) {
		painter.addDrawable(d);
	}

}
